package com.wmjsoft.plugins.security.base

import com.wmjsoft.plugins.nutzplugin.dao.MyDao
import org.nutz.dao.Chain
import org.nutz.dao.Cnd
import org.slf4j.LoggerFactory
import java.util.*
import java.util.stream.Collectors.toList
import kotlin.Comparator
import kotlin.collections.ArrayList

/**
 * 如果我没有写注释，请提醒我
 * @program: bridge
 * @description: 树形基类
 * @author: Mr.Lee
 * @create: 2020-10-12 10:33
 */
@Suppress("UNCHECKED_CAST")
abstract class MyBaseTreeService<T : Any, TREE : Any> : MyBaseService<T>() {
    val log = LoggerFactory.getLogger(this.javaClass)
    abstract fun getTreeClass(): Class<TREE>



    fun saveOrUpdate(t: T): Long {
        val id = if(null == ClassUtils.getProperty(t,"id") ) 0 else ClassUtils.getProperty(t,"id") as Long
        val parentId = ClassUtils.getProperty(t, "parentId") as Long
        var parent: T? = null
        if (parentId != 0L) {
            parent = isParentIdValid(parentId)
            isFalse(parent != null, "无效的父级id")
        }
        if (0L == id) {
            InitCreateUpdateUser.initCreateUser(t)
            if (parentId > 0) {
                MyDao.dao.update(this.getClazzT(), Chain.makeSpecial("hasChild", "+1"), Cnd.where("id", "=", parentId))
                val lay = if (null == parent) -1 else ClassUtils.getProperty(parent, "lay") as Int
                ClassUtils.setProperty(t, "lay", lay + 1)
            }
        } else {
            this.update(t)
            CurrentThreadVariable.clearCurrentThreadVariable()
            return id
        }
        return ClassUtils.getProperty(save(t), "id") as Long
    }

    private fun update(t: T) {
        InitCreateUpdateUser.initLastUpdateUser(t)
        val id: Long = ClassUtils.getProperty(t, "id") as Long
        //获取还未更改的自身
        val itSelf = super.getInfoById(id)
        //获得本次更改后的parentId
        val updateAfterParentId = ClassUtils.getProperty(t, "parentId") as Long
        //获得未更改的parentId
        val parentId = ClassUtils.getProperty(itSelf, "parentId") as Long

        if (parentId != null && parentId == updateAfterParentId) {
            //表示没有更改父级id
            var currentThreadVariable = CurrentThreadVariable.getCurrentThreadVariable()
            if (currentThreadVariable != null) {
                currentThreadVariable += 1
                ClassUtils.setProperty(t, "lay", currentThreadVariable)
                this.updateById(t)
                CurrentThreadVariable.setCurrentThreadVariable(currentThreadVariable)
            } else {
                this.updateById(t)
            }

            val subsetOfItSelf = MyDao.find(getClazzT()).where().eq("parentId", id).list(getClazzT())
            if (subsetOfItSelf!!.isNotEmpty()) {
                //如果有子集需要走自身递归，更改lay
                for (item in subsetOfItSelf) {
                    this.update(item)
                }
            }
            return
        }
        //如果更改了子父的关系
        //1 查看原来父级是否有其他子集
        val subsetOfOriginalParents = MyDao.find(getClazzT()).where().eq("parentId", parentId).list(getClazzT())
        isEmpty(subsetOfOriginalParents!!, "树形结构出现异常，请检查数据")
        if (subsetOfOriginalParents.size == 1) {
            MyDao.dao.update(this.getClazzT(), Chain.makeSpecial("hasChild", "0"), Cnd.where("id", "=", parentId))
        } else {
            //hasChild -1
            MyDao.dao.update(this.getClazzT(), Chain.makeSpecial("hasChild", "-1"), Cnd.where("id", "=", parentId))
        }

        //2. 查看更改后的父级
        val updateAfterParent = super.getInfoById(updateAfterParentId)
        val lay = if (updateAfterParent == null) -1 else ClassUtils.getProperty(updateAfterParent, "lay") as Int
        MyDao.dao.update(this.getClazzT(), Chain.makeSpecial("hasChild", "+1"), Cnd.where("id", "=", updateAfterParentId))
        ClassUtils.setProperty(t, "lay", lay + 1)
        this.updateById(t)
        CurrentThreadVariable.setCurrentThreadVariable(lay + 1)
        //2。1 查找本身的子集
        val subsetItSelf = MyDao.find(this.getClazzT()).where().eq("parentId", id).list(this.getClazzT())
        if (subsetItSelf!!.isNotEmpty()) {
            for (item in subsetItSelf) {
                this.update(item)
            }
        }

    }

    fun treeDelete(id: Long): StringResult? {
        val sub = MyDao.find(this.getClazzT()).where().eq("parentId", id).list(this.getClazzT())
        if (sub!!.isEmpty()) {
            throw RuntimeException("请先删除子类")
        }
        val itSelf = this.getInfoById(id)
        try {
            val parent = this.getInfoById(ClassUtils.getProperty(itSelf, "parentId") as Long)
            if (null != parent) {
                MyDao.dao.update(this.getClazzT(),
                        Chain.makeSpecial("hasChild", "-1"),
                        Cnd.where("id", "=", ClassUtils.getProperty(itSelf, "parentId") as Long))
            }
            this.lDeleteById(id)
            return StringResult.getInstance("删除成功")
        } catch (e: Exception) {
            log.error("------------------------>>>>>>>>>删除出现异常${id}")
            throw RuntimeException("删除出现异常")
        }
    }

    fun findTree(cnd: Cnd,out: Class<TREE>): List<TREE> {
        cnd.and("deleteStatus", "=", 0)
        val ts = MyDao.dao.query(this.getClazzT(), cnd)
        if (ts.isEmpty()) {
            return ArrayList<TREE>()
        }
        val trees = tConversionDto<T,TREE>(ts, getTreeClass())


        val map = trees.groupBy {
            ClassUtils.getProperty(it, "lay") as Int
        }
        try {
            val lays = map!!.keys.stream().sorted(Comparator.comparingInt(Int::toInt)).collect(toList())
            val parentGroup = trees
                    .stream().distinct().collect(toList())
                    .groupBy { ClassUtils.getProperty(it, "parentId") as Long }
            return setChild(parentGroup as Map<Long, List<TREE>>, parentGroup[lays[0].toLong()] as List<TREE>)

        } catch (e: Exception) {
            log.error("--------------------------->>>>>>>>>拼接树形发生异常")
            throw RuntimeException("树形拼装发生异常")
        }
    }

    private fun setChild(parentGroup: Map<Long, List<TREE>>, list: List<TREE>): List<TREE> {
        list.forEach {
            val id = ClassUtils.getProperty(it, "id") as Long
            if(null == id) {
                log.error("-------------------------->>>>>>>>>>树形拼接发生异常--myBaseTessService 报错代码行数 153")
                throw RuntimeException("树形拼装失败")
            }
            val trees = parentGroup[id]
            ClassUtils.setProperty(it,"children", trees)
            if(null != trees && trees.isNotEmpty()){
                this.setChild(parentGroup,trees)
            }
        }
        return list
    }

    private fun isParentIdValid(parentId: Any): T? {
        return super.getInfoById(parentId as Long)
    }
}